<?php

namespace app\api\controller;

use app\common\controller\Api;
use think\Cache;

use Web3\Web3;
use Web3\Utils;
use Elliptic\EC;
use Web3\Contract;
use kornrunner\Keccak;
use GuzzleHttp\Client;
use app\common\model\Callback;
use app\common\model\KeyStore;
use app\common\model\Credential;

use app\common\model\RawTxBuilder;
use app\common\model\EthApiWeb3;


// 助记词生成地址私钥
use BitWasp\Bitcoin\Bitcoin;
use BitWasp\Bitcoin\Crypto\Random\Random;
use BitWasp\Bitcoin\Key\Factory\HierarchicalKeyFactory;
use BitWasp\Bitcoin\Mnemonic\Bip39\Bip39Mnemonic;
use BitWasp\Bitcoin\Mnemonic\Bip39\Bip39SeedGenerator;
use BitWasp\Bitcoin\Mnemonic\MnemonicFactory;

// 授权验证
use app\api\controller\nozzle\Empower;

/**
 * 以太坊接口
 */
class Eth extends Api
{
    protected $noNeedLogin = ['*'];
    protected $noNeedRight = ['*'];
    // protected $host = 'http://localhost:7545';
    protected $host = 'https://mainnet.infura.io/v3/a8d2b33a912f4a1fb0093149be8fce98';
    // protected $host = 'http://47.110.224.202:6666';
    // protected $host = 'https://rinkeby.infura.io/v3/407e0366654346f1bb013f2dc1ff1c09'; //测试网络
    protected $api_host = 'http://api-cn.etherscan.com/api?apikey=EDDAB17BWIDFW3DB3ZNUSIW9RRAUP3K4TK&'; // eth官网中国站（可提供接口）
    protected $chainId = 1;  // 使用rinkeby测试链: 1-mainnet 3-ropsten 4-rinkeby

    protected function _initialize()
    {
        parent::_initialize();

        // $empower = new Empower();
        // $apikey = input('apikey');
        // if(request()->ip() != '127.0.0.1'){
        //   $empower->check_apikey($apikey);
        // }

        // if(!$apikey){
        //     $this->error('请输入授权码');
        // }
        // $auth_code = config('auth_code');
        // $row = [];
        // foreach($auth_code as $v){
        //     if($v['apikey'] ==  $apikey){
        //         $row = $v;
        //     }
        // }
        // if(!$row){
        //     $this->error('授权码不正确');
        // }
        // if(strtotime($row['time']) < time()){
        //     $this->error('授权码已过期，请联系管理员');
        // }

        // if($row['white_list_ip']){
        //     $white_list_ip = explode(',', $row['white_list_ip']);
        //     if(!in_array(request()->ip(),$white_list_ip)){
        //         $this->error('该IP禁止请求');
        //     }
        // }

        $this->eth = new EthApiWeb3($this->host);
    }

    /**
    * 当前的客户端版本
    * @ApiSummary  (查询当前的客户端版本)
    * @ApiMethod   (GET)
    * @ApiRoute    (/api/eth/clientversion)
    * @ApiReturn   ({"code":1,"msg":"返回成功","time":"1576564627","data":"EthereumJS TestRPC\/v2.8.0\/ethereum-js"})
    *
    */
    public function clientVersion()
    {   

        // $opts = [
        //       'jsonrpc' => '2.0',
        //       'method' => 'web3_clientVersion',
        //       'params' => [],
        //       'id' => time()
        //   ];
        // // $data = array("name" => "Hagrid", "age" => "36");
        // $data_string = json_encode($opts);
        
        // $ch = curl_init($this->host);
        // curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
        // curl_setopt($ch, CURLOPT_POSTFIELDS,$data_string);
        // curl_setopt($ch, CURLOPT_RETURNTRANSFER,true);
        // curl_setopt($ch, CURLOPT_HTTPHEADER, array(
        //     'Content-Type: application/json',
        //     'Content-Length: ' . strlen($data_string))
        // );
        
        // $result = curl_exec($ch);
        // // $result = http($this->host,json_encode($opts),'POST');
        // var_dump($result);die;


        $return =  $this->eth->clientVersion();

        
        $this->success('返回成功',$return);
    }

    /**
    * 查询以太坊网络手续费
    * @ApiSummary  (查询以太坊网络手续费,返回当前的gas价格，单位：wei。)
    * @ApiMethod   (GET)
    * @ApiRoute    (/api/eth/apigasPrice)
    * @ApiReturn   ({"code":1,"msg":"返回成功","time":"1576568627","data":20000000000})
    *
    */
    public function apigasPrice()
    {
        $gasPrice = $this->eth->gasPrice();
        $this->success('返回成功',$gasPrice);
    }

    /**
    * 查询余额
    * @ApiSummary  (查询余额)
    * @ApiMethod   (GET)
    * @ApiRoute    (/api/eth/getbalance)
    * @ApiParams   (name="address", type="string", required=true, description="账户地址")
    * @ApiReturn   ({"code":1,"msg":"返回成功","time":"1576908681","data":100})
    * @ApiReturnParams   (name="code", type="integer", required=true, description="1")
    * @ApiReturnParams   (name="msg", type="string", required=true, description="返回成功")
    * @ApiReturnParams   (name="data", type="integer", description="余额ETH")
    *
    */
    public function getBalance()
    {
        $address =input('address');
        $getbalance = $this->eth->getBalance($address);
        // 获取wei单位，精度18位，小数取6位
        $getbalance = bcdiv($getbalance, bcpow(10,18),6);
        $this->success('返回成功',$getbalance);
    }

    /**
    * 交易明细
    * @ApiSummary  (返回指定哈希对应的交易详情,所谓订单号查询)
    * @ApiMethod   (GET)
    * @ApiRoute    (/api/eth/gettransactionbyhash)
    * @ApiParams   (name="hash", type="string", required=true, description="交易哈希")
    * @ApiReturnParams   (name="code", type="integer", required=true, description="1")
    * @ApiReturnParams   (name="msg", type="string", required=true, description="返回成功")
    * @ApiReturnParams   (name="data", type="object", description="交易收据对象，如果收据不存在则为null。交易对象的结构如下")
    * @ApiReturnParams   (name="hash", type="string", description="DATA, 32字节 - 交易哈希")
    * @ApiReturnParams   (name="nonce", type="string", description="QUANTITY - 本次交易之前发送方已经生成的交易数量")
    * @ApiReturnParams   (name="blockHash", type="string", description="DATA, 32字节 - 交易所在块的哈希，对于挂起块，该值为null")
    * @ApiReturnParams   (name="blockNumber", type="string", description="QUANTITY - 交易所在块的编号，对于挂起块，该值为null")
    * @ApiReturnParams   (name="transactionIndex", type="string", description="QUANTITY - 交易在块中的索引位置，挂起块该值为null")
    * @ApiReturnParams   (name="from", type="string", description="DATA, 20字节 - 交易发送方地址")
    * @ApiReturnParams   (name="to", type="string", description="DATA, 20字节 - 交易接收方地址，对于合约创建交易，该值为null")
    * @ApiReturnParams   (name="value", type="string", description="QUANTITY - 发送的以太数量，单位：wei")
    * @ApiReturnParams   (name="gasPrice", type="string", description="QUANTITY - 发送方提供的gas价格，单位：wei")
    * @ApiReturnParams   (name="gas", type="string", description="QUANTITY - 发送方提供的gas可用量")
    * @ApiReturnParams   (name="input", type="string", description="DATA - 随交易发送的数据")
    * @ApiReturn   ({"code":1,"msg":"返回成功","time":"1576571765","data":{"hash":"0x4af98db70e3a33d449c78a3cc814b5caf1148a69e565b34e8342a2d7076035e6","nonce":"0x0","blockHash":"0x433901e3f5dd3be8f494260a4ec72892513b651c65f2a4f8bac2ebffd31b00c2","blockNumber":"0x1","transactionIndex":"0x0","from":"0xea67905ee09380f08bf98bbae02d74774e0aa25e","to":"0xc7b4a460dcd39dcbea54ebe074c69ec764634285","value":"0xde0b6b3a7640000","gas":"0x15f90","gasPrice":"0x4a817c800","input":"0x","v":"0x1b","r":"0xbc8ec41487d3c3a22e64271739e75b01666abdc51aa8dcb1a438e018749e3c80","s":"0xcde7b1ade7140cdbbe1933a8c1707b6a77a14f7917ed12405a057b5b283fef1"}})
    *
    */
    public function getTransactionByHash()
    {
        $hash = input('hash');
        $result = $this->eth->getTransactionByHash($hash);
        $this->success('返回成功',$result);
    }

    /**
    * 交易明细(收据)
    * @ApiSummary  (返回指定交易的收据，使用哈希指定交易)
    * @ApiMethod   (GET)
    * @ApiRoute    (/api/eth/gettransactionreceipt)
    * @ApiParams   (name="hash", type="string", required=true, description="交易哈希")
    * @ApiReturnParams   (name="code", type="integer", required=true, description="1")
    * @ApiReturnParams   (name="msg", type="string", required=true, description="返回成功")
    * @ApiReturnParams   (name="data", type="object", description="交易收据对象，如果收据不存在则为null。交易对象的结构如下")
    * @ApiReturnParams   (name="transactionHash", type="string", description="DATA, 32字节 - 交易哈希")
    * @ApiReturnParams   (name="transactionIndex", type="string", description="QUANTITY - 交易在块内的索引序号")
    * @ApiReturnParams   (name="blockHash", type="string", description="DATA, 32字节 - 交易所在块的哈希")
    * @ApiReturnParams   (name="blockNumber", type="string", description="QUANTITY - 交易所在块的编号")
    * @ApiReturnParams   (name="from", type="string", description="DATA, 20字节 - 交易发送方地址")
    * @ApiReturnParams   (name="to", type="string", description="DATA, 20字节 - 交易接收方地址，对于合约创建交易该值为null")
    * @ApiReturnParams   (name="cumulativeGasUsed", type="string", description="QUANTITY - 交易所在块消耗的gas总量")
    * @ApiReturnParams   (name="gasUsed", type="string", description="QUANTITY - 该次交易消耗的gas用量")
    * @ApiReturnParams   (name="contractAddress", type="string", description="DATA, 20字节 - 对于合约创建交易，该值为新创建的合约地址，否则为null")
    * @ApiReturnParams   (name="logs", type="string", description="Array - 本次交易生成的日志对象数组")
    * @ApiReturnParams   (name="logsBloom", type="string", description="DATA, 256字节 - bloom过滤器，轻客户端用来快速提取相关日志")
    * @ApiReturnParams   (name="root", type="string", description="DATA 32字节，后交易状态根(pre Byzantium)")
    * @ApiReturnParams   (name="status", type="string", description="QUANTITY ，1 (成功) 或 0 (失败)")
    * @ApiReturn   ({"code":1,"msg":"返回成功","time":"1576635215","data":{"transactionHash":"0x4af98db70e3a33d449c78a3cc814b5caf1148a69e565b34e8342a2d7076035e6","transactionIndex":"0x0","blockHash":"0x7ab6f2c8edc7761c41818007bd1a3a9c6534a5c213ef82d6db2095035cead904","blockNumber":"0x1","from":"0xea67905ee09380f08bf98bbae02d74774e0aa25e","to":"0xc7b4a460dcd39dcbea54ebe074c69ec764634285","gasUsed":"0x5208","cumulativeGasUsed":"0x5208","contractAddress":null,"logs":[],"status":"0x1","logsBloom":"0x000...","v":"0x1b","r":"0xbc8ec41487d3c3a22e64271739e75b01666abdc51aa8dcb1a438e018749e3c80","s":"0xcde7b1ade7140cdbbe1933a8c1707b6a77a14f7917ed12405a057b5b283fef1"}})
    *
    */
    public function getTransactionReceipt()
    {
        $hash = input('hash');
        $result =$this->eth->getTransactionReceipt($hash);

        if($result){
            $this->success('返回成功',$result);
        }

        $this->error('还在交易中...');


    }

    /**
    * 返回指定地址发生的交易数量
    * @ApiSummary  (返回指定地址发生的交易数量,该账号地址订单总数量)
    * @ApiMethod   (GET)
    * @ApiRoute    (/api/eth/gettransactioncount)
    * @ApiParams   (name="address", type="string", required=true, description="账户地址")
    * @ApiReturnParams   (name="code", type="integer", required=true, description="1")
    * @ApiReturnParams   (name="msg", type="string", required=true, description="返回成功")
    * @ApiReturnParams   (name="data", type="integer", description="QUANTITY - 从指定地址发出的交易数量，整数")
    * @ApiReturn   ({"code":1,"msg":"返回成功","time":"1576637633","data":8})
    *
    */
    public function getTransactionCount()
    {
        $address = input('address');
        $result  = $this->eth->getTransactionCount($address);
        $this->success('返回成功',$result);
    }

    /**
    * 查询ETH交易记录
    * @ApiSummary  (查询ETH交易记录，所谓账户ETH交易记录)
    * @ApiMethod   (GET)
    * @ApiRoute    (/api/eth/transactions)
    * @ApiParams   (name="address", type="string", required=true, description="账户地址")
    * @ApiParams   (name="page", type="integer", required=false, description="页数，默认第1页")
    * @ApiParams   (name="offset", type="integer", required=false, description="每页的显示多少条，默认20条")
    * @ApiReturnParams   (name="code", type="integer", required=true, description="1")
    * @ApiReturnParams   (name="msg", type="string", required=true, description="返回成功")
    * @ApiReturnParams   (name="data", type="object", description="ETH交易记录数组对象，如果没有不存在则为null。ETH交易记录结构如下")
    * @ApiReturnParams   (name="blockHash", type="string", description="区块哈希值")
    * @ApiReturnParams   (name="blockNumber", type="string", description="区块编号")
    * @ApiReturnParams   (name="confirmations", type="string", description="交易确认数")
    * @ApiReturnParams   (name="contractAddress", type="string", description="合约地址")
    * @ApiReturnParams   (name="cumulativeGasUsed", type="string", description="区块累计交易用量")
    * @ApiReturnParams   (name="from", type="string", description="发起账号")
    * @ApiReturnParams   (name="gas", type="string", description="gas用量")
    * @ApiReturnParams   (name="gasPrice", type="string", description="gas价格")
    * @ApiReturnParams   (name="gasUsed", type="string", description="本交易的gas用量")
    * @ApiReturnParams   (name="hash", type="string", description="交易哈希")
    * @ApiReturnParams   (name="input", type="string", description="交易附加数据，16进制字符串编码")
    * @ApiReturnParams   (name="isError", type="string", description="是否发生错误，0表示没有错误，1表示发生错误")
    * @ApiReturnParams   (name="nonce", type="string", description="nonce值")
    * @ApiReturnParams   (name="timeStamp", type="string", description="区块时间戳")
    * @ApiReturnParams   (name="to", type="string", description="接收账号")
    * @ApiReturnParams   (name="transactionIndex", type="string", description="索引序号")
    * @ApiReturnParams   (name="txreceipt_status", type="string", description="交易收据状态")
    * @ApiReturnParams   (name="value", type="string", description="交易金额")
    * @ApiReturn   ({"code":1,"msg":"返回成功","time":"1576835648","data":[{"blockNumber":"0","timeStamp":"1438269973","hash":"GENESIS_ddbd2b932c763ba5b1b7ae3b362eac3e8d40121a","nonce":"","blockHash":"","transactionIndex":"0","from":"GENESIS","to":"0xddbd2b932c763ba5b1b7ae3b362eac3e8d40121a","value":"10000000000000000000000","gas":"0","gasPrice":"0","isError":"0","txreceipt_status":"","input":"","contractAddress":"","cumulativeGasUsed":"0","gasUsed":"0","confirmations":"9135057"}]})
    *
    */
    public function Transactions()
    {

        $address = input('address');
        $page = input('page');
        $offset = input('offset');
        $page = empty($page) ? 1 : $page;
        $offset = empty($offset) ? 20 : $offset;
        $url = ethapi()."module=account&action=txlist&sort=desc&address={$address}&page={$page}&offset={$offset}";

        try {
            $result =  json_decode(file_get_contents($url),true);
        } catch (\Throwable $th) {
            trace($th->getMessage(),'查询ETH交易记录error');
            $this->error('系统错误，请联系管理员');
        }

        if ($result['status'] == 0) {
            $this->error($result['message']);
        }
        $this->success('返回成功',$result['result']);
    }

    /**
    * 查询REC20交易记录
    * @ApiSummary  (查询REC20交易记录，所谓账户代币交易记录)
    * @ApiMethod   (GET)
    * @ApiRoute    (/api/eth/erctransactions)
    * @ApiParams   (name="address", type="string", required=true, description="账户地址")
    * @ApiParams   (name="contractaddress", type="string", required=true, description="合约地址")
    * @ApiParams   (name="page", type="integer", required=false, description="页数，默认第1页")
    * @ApiParams   (name="offset", type="integer", required=false, description="每页的显示多少条，默认20条")
    * @ApiReturnParams   (name="code", type="integer", required=true, description="1")
    * @ApiReturnParams   (name="msg", type="string", required=true, description="返回成功")
    * @ApiReturnParams   (name="data", type="object", description="REC20交易记录数组对象，如果没有不存在则为null。REC20交易记录结构如下")
    * @ApiReturnParams   (name="blockHash", type="string", description="区块哈希值")
    * @ApiReturnParams   (name="blockNumber", type="string", description="区块编号")
    * @ApiReturnParams   (name="confirmations", type="string", description="交易确认数")
    * @ApiReturnParams   (name="contractAddress", type="string", description="合约地址")
    * @ApiReturnParams   (name="cumulativeGasUsed", type="string", description="区块累计交易用量")
    * @ApiReturnParams   (name="from", type="string", description="发起账号")
    * @ApiReturnParams   (name="gas", type="string", description="gas用量")
    * @ApiReturnParams   (name="gasPrice", type="string", description="gas价格")
    * @ApiReturnParams   (name="gasUsed", type="string", description="本交易的gas用量")
    * @ApiReturnParams   (name="hash", type="string", description="交易哈希")
    * @ApiReturnParams   (name="input", type="string", description="交易附加数据，16进制字符串编码")
    * @ApiReturnParams   (name="nonce", type="string", description="nonce值")
    * @ApiReturnParams   (name="timeStamp", type="string", description="区块时间戳")
    * @ApiReturnParams   (name="to", type="string", description="接收账号")
    * @ApiReturnParams   (name="tokenDecimal", type="string", description="精确小数点")
    * @ApiReturnParams   (name="tokenName", type="string", description="代币名称")
    * @ApiReturnParams   (name="tokenSymbol", type="string", description="标记符号")
    * @ApiReturnParams   (name="transactionIndex", type="string", description="索引序号")
    * @ApiReturnParams   (name="value", type="string", description="交易金额")
    * @ApiReturn   ({"code":1,"msg":"返回成功","time":"1576832876","data":[{"blockNumber":"4730207","timeStamp":"1513240363","hash":"0xe8c208398bd5ae8e4c237658580db56a2a94dfa0ca382c99b776fa6e7d31d5b4","nonce":"406","blockHash":"0x022c5e6a3d2487a8ccf8946a2ffb74938bf8e5c8a3f6d91b41c56378a96b5c37","from":"0x642ae78fafbb8032da552d619ad43f1d81e4dd7c","contractAddress":"0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2","to":"0x4e83362442b8d1bec281594cea3050c8eb01311c","value":"5901522149285533025181","tokenName":"Maker","tokenSymbol":"MKR","tokenDecimal":"18","transactionIndex":"81","gas":"940000","gasPrice":"32010000000","gasUsed":"77759","cumulativeGasUsed":"2523379","input":"deprecated","confirmations":"4404709"}]})
    *
    */
    public function ErcTransactions()
    {
        $address = input('address');
        $contractaddress = input('contractaddress');
        $page = input('page');
        $offset = input('offset');

        $page = empty($page) ? 1 : $page;
        $offset = empty($offset) ? 20 : $offset;
        $url = ethapi()."module=account&action=tokentx&contractaddress={$contractaddress}&address={$address}&page={$page}&offset={$offset}";

        dump(get_data($url));die;
        try {
            $result =  json_decode(file_get_contents($url),true);
        } catch (\Throwable $th) {
            trace($th->getMessage(),'查询REC20交易记录error');
            $this->error('系统错误，请联系管理员');
        }

        if ($result['status'] == 0) {
            $this->error($result['message']);
        }
        $this->success('返回成功',$result['result']);
    }


    /**
    * ETH创建钱包
    * @ApiSummary  (ETH创建钱包,创建后建议保存私钥，相当于银行卡的密码)
    * @ApiMethod   (GET)
    * @ApiRoute    (/api/eth/CreateEtherWallet)
    * @ApiReturnParams   (name="code", type="integer", required=true, description="1")
    * @ApiReturnParams   (name="msg", type="string", required=true, description="返回成功")
    * @ApiReturnParams   (name="data", type="object", description="创建钱包对象，如果创建失败不存在则为null。创建钱包结构如下")
    * @ApiReturnParams   (name="PrivateKey", type="string", description="私钥")
    * @ApiReturnParams   (name="PublicKey", type="string", description="公钥")
    * @ApiReturnParams   (name="address", type="string", description="账号地址")
    * @ApiReturn   ({"code":1,"msg":"返回成功","time":"1576659382","data":{"PrivateKey":"58bf1e81eec57193224bf45c4cc1a8a5730ff316c395a9f28693ad9800318e5f","PublicKey":"04a3a420aab14163cc800cb70b5b997a8b337b1f0a1cd06fdc14239a6d112b0c8784f1f4172bf40b5ec19db6850a43ee0364c0bff2fc4387bd5484f73ad355d639","address":"0xf661dced8cad76aadf5af403754bb171715e675d"}})
    *
    */
    public function CreateEtherWallet()
    {

        $ec = new EC('secp256k1');
        // 指定私钥//
        // $keyPair = $ec->keyFromPrivate('2111');
        while (true) {
            // 秘钥对
            $keyPair = $ec->genKeyPair();
            // 私钥生成
            $privateKey = $keyPair->getPrivate()->toString(16,2);
            if(strlen($privateKey) == 64){
                break;
            }
        }

        // 公钥生成
        $publicKey = $keyPair->getPublic()->encode('hex');
        // 账号地址生成
        $address = '0x' . substr(Keccak::hash(substr(hex2bin($publicKey),1),256),24);
        $wfn = KeyStore::saveImport($privateKey,'123',dirname(__FILE__) .'/KeyStore');

        $data = [
            'PrivateKey' => $privateKey,
            'PublicKey' => $publicKey,
            'address' => $address,
        ];

        $this->success('返回成功',$data);
    }

    /**
    * ETH创建钱包并生成助记词
    * @ApiSummary  (ETH创建钱包,创建后建议保存私钥或助记词，相当于银行卡的密码)
    * @ApiMethod   (GET)
    * @ApiRoute    (/api/eth/CreateEtherWalletMnemonic)
    * @ApiReturnParams   (name="code", type="integer", required=true, description="1")
    * @ApiReturnParams   (name="msg", type="string", required=true, description="返回成功")
    * @ApiReturnParams   (name="data", type="object", description="创建钱包对象，如果创建失败不存在则为null。创建钱包结构如下")
    * @ApiReturnParams   (name="mnemonic", type="string", description="助记词")
    * @ApiReturnParams   (name="PrivateKey", type="string", description="私钥")
    * @ApiReturnParams   (name="PublicKey", type="string", description="公钥")
    * @ApiReturnParams   (name="address", type="string", description="账号地址")
    * @ApiReturn   ({"code":1,"msg":"返回成功","time":"1592307200","data":{"mnemonic":"oak vivid tell acoustic link grow stamp april smooth great sphere naive","PrivateKey":"5aac0f9fc41629758dc3c92ad717561e0f5786a07b0b602dbb1cd440840b6559","PublicKey":"0441989bbde464b5dbceb4d03415baeb6b82ad8b62492b965cfcc9254c66afbb79889391440e8f3cd4c91bf4cdc9ac21f21b5a66f394ec57dafdc0a218b3a7389b","address":"0x4378d0dbd7b4fb46ce29d0e655a88a2684b152a3"}})
    *
    */
    private function CreateEtherWalletMnemonic()
    {

        // Bip39
        $math = Bitcoin::getMath();
        $network = Bitcoin::getNetwork();
        $random = new Random();
        // 生成随机数(initial entropy)
        $entropy = $random->bytes(Bip39Mnemonic::MIN_ENTROPY_BYTE_LEN);
        $bip39 = MnemonicFactory::bip39();
        // 通过随机数生成助记词
        $mnemonic = $bip39->entropyToMnemonic($entropy);
        $seedGenerator = new Bip39SeedGenerator();
        // 通过助记词生成种子，传入可选加密串'hello'
        $seed = $seedGenerator->getSeed($mnemonic);
        // echo "seed: " . $seed->getHex() . PHP_EOL;
        $hdFactory = new HierarchicalKeyFactory();
        $master = $hdFactory->fromEntropy($seed);
        // 设置路径account
        $hardened = $master->derivePath("44'/60'/0'/0/0");
        $keyFromPrivate = $hardened->getPrivateKey()->getHex();

        $ec = new EC('secp256k1');
        // 指定私钥//
        $keyPair = $ec->keyFromPrivate($keyFromPrivate);
        $privateKey = $keyPair->getPrivate()->toString(16,2);

        // 公钥生成
        $publicKey = $keyPair->getPublic()->encode('hex');
        // 账号地址生成
        $address = '0x' . substr(Keccak::hash(substr(hex2bin($publicKey),1),256),24);
        $wfn = KeyStore::saveImport($privateKey,'123',dirname(__FILE__) .'/KeyStore');
        $data = [
            'mnemonic' => $mnemonic,
            'PrivateKey' => $privateKey,
            'PublicKey' => $publicKey,
            'address' => $address,
        ];

        $this->success('返回成功',$data);
    }


    /**
    * 导入以太坊钱包
    * @ApiSummary  (导入以太坊钱包,创建后建议保存私钥，相当于银行卡的密码)
    * @ApiMethod   (GET)
    * @ApiRoute    (/api/eth/importethwallet)
    * @ApiParams   (name="privatekey", type="string", required=true, description="你的私钥")
    * @ApiReturnParams   (name="code", type="integer", required=true, description="1")
    * @ApiReturnParams   (name="msg", type="string", required=true, description="返回成功")
    * @ApiReturnParams   (name="data", type="object", description="导入以太坊钱包返回对象，如果导入失败不存在则为null。导入钱包结构如下")
    * @ApiReturnParams   (name="PrivateKey", type="string", description="私钥")
    * @ApiReturnParams   (name="PublicKey", type="string", description="公钥")
    * @ApiReturnParams   (name="address", type="string", description="账号地址")
    * @ApiReturn   ({"code":1,"msg":"返回成功","time":"1576833785","data":{"PrivateKey":"caa5c611828cd7e8854cfe1cde6d54e6ecc5a673e742e36479797f99e422cca8","PublicKey":"04a36ea424b83c179d389a6c39fc1af7b07f1def4d347a6c93d94048d7d5c4837495fac856414bbd80d97be5e7e21bf94691884e69787701ad79a1056ce356c025","address":"0xf0e7d0ddd4411d01860f83e77c652c86ae31eb48"}})
    *
    */
    public function ImportEthWallet()
    {
        // $wfn = dirname(__FILE__) .'\KeyStore\40845e44f10351a6c41875c3ff536663203dd9d8.json';
        // $recovered = KeyStore::load('123',$wfn);
        // dump($recovered);die;

        $private_key = input('privatekey');
        if (!$private_key) {
            $this->error('必选参数');
        }

        $ec = new EC('secp256k1');
        // 指定私钥
        $keyPair = $ec->keyFromPrivate($private_key);
        // $keyPair = $ec->keyFromPrivate($privateKey);
        // 私钥生成
        $privateKey = $keyPair->getPrivate()->toString(16,2);
        // 公钥生成
        $publicKey = $keyPair->getPublic()->encode('hex');
        // 账号地址生成
        $address = '0x' . substr(Keccak::hash(substr(hex2bin($publicKey),1),256),24);
        $data = [
            'PrivateKey' => $privateKey,
            'PublicKey' => $publicKey,
            'address' => $address,
        ];
         // 将私钥存入钱包 $wfn返回地址路径
        $wfn = KeyStore::saveImport($privateKey,'123',dirname(__FILE__) .'\KeyStore');
        //获取钱包,返回私钥
        // $recovered = KeyStore::load('123',$wfn);

        if (empty($wfn)) {
            $this->error('导入失败');
        }
        $this->success('返回成功',$data);
    }

    /**
    * 转账
    * @ApiSummary  (转账)
    * @ApiMethod   (POST)
    * @ApiRoute    (/api/eth/transferaccounts)
    * @ApiParams   (name="privatekey", type="string", required=true, description="私钥")
    * @ApiParams   (name="value", type="string", required=true, description="交易ETH数量")
    * @ApiParams   (name="to", type="string", required=true, description="收款账号")
    * @ApiParams   (name="gasprice", type="string", required=false, description="手续费参考0.00042 ETH‬")
    * @ApiReturnParams   (name="code", type="integer", required=true, description="1")
    * @ApiReturnParams   (name="msg", type="string", required=true, description="返回成功")
    * @ApiReturnParams   (name="data", type="object", description="交易收据对象，如果收据不存在则为null。交易对象的结构如下")
    * @ApiReturnParams   (name="transactionHash", type="string", description="DATA, 32字节 - 交易哈希")
    * @ApiReturnParams   (name="transactionIndex", type="string", description="QUANTITY - 交易在块内的索引序号")
    * @ApiReturnParams   (name="blockHash", type="string", description="DATA, 32字节 - 交易所在块的哈希")
    * @ApiReturnParams   (name="blockNumber", type="string", description="QUANTITY - 交易所在块的编号")
    * @ApiReturnParams   (name="from", type="string", description="DATA, 20字节 - 交易发送方地址")
    * @ApiReturnParams   (name="to", type="string", description="DATA, 20字节 - 交易接收方地址，对于合约创建交易该值为null")
    * @ApiReturnParams   (name="cumulativeGasUsed", type="string", description="QUANTITY - 交易所在块消耗的gas总量")
    * @ApiReturnParams   (name="gasUsed", type="string", description="QUANTITY - 该次交易消耗的gas用量")
    * @ApiReturnParams   (name="contractAddress", type="string", description="DATA, 20字节 - 对于合约创建交易，该值为新创建的合约地址，否则为null")
    * @ApiReturnParams   (name="logs", type="string", description="Array - 本次交易生成的日志对象数组")
    * @ApiReturnParams   (name="logsBloom", type="string", description="DATA, 256字节 - bloom过滤器，轻客户端用来快速提取相关日志")
    * @ApiReturnParams   (name="status", type="string", description="QUANTITY ，1 (成功) 或 0 (失败)")
    * @ApiReturn   ({"code":1,"msg":"返回成功","time":"1577070382","data":{"transactionHash":"0xf4b874f1947ebdc7d92de4301bc4811385076ca3d6b416b29758f2f2680e52d7","transactionIndex":"0x0","blockHash":"0xf4870b88926b57aaccd88356dc163e5631ce1a0f66637f91a38894f9967af575","blockNumber":"0x8","from":"0x17533f2b9a6ae19554ab2bbbfb3b326700fe9919","to":"0x8e49e51c7fa791e1d9eb833c13eb784e9d21f981","gasUsed":"0x535c","cumulativeGasUsed":"0x535c","contractAddress":null,"logs":[],"status":"0x1","logsBloom":"0x000...","v":"0x37","r":"0x2cea2dd2225764dc04248faf79cf0d4b7b1f75856d908681ecd808445fdeaff1","s":"0x226f7215f13ff1959c5f5b20b3c6c73cea06e34976a590450425aa39c0d2363f"}})
    *
    */
    public function Transferaccounts()
    {
        $privatekey = input('privatekey');
        $value = input('value');
        $gasprice = input('gasprice');
        $to = input('to');
        // 使用私钥创建Credential对象
        $credential = Credential::fromKey($privatekey);
        // 判断是否满足条件

        //单位
        if(!$gasprice){
            //单位
            $gasprice = $this->eth->gasPrice();
        }else{
             // $gasprice = '10000000000';
             $gasprice = strval(bcdiv(($gasprice * bcpow(10,9) ),21000) * bcpow(10,9));
        }


        $this->Transferrequest($credential->getAddress(),$value,'ETH',$gasprice, 21000);
        $receipt =  $this->eth->Transferaccounts($credential,$value,$to, $gasprice, $this->chainId);

        if ($receipt) {
        $this->success('转账成功',$receipt);
        }
        $this->success('转账失败');

    }

    /**
    * 代币转账
    * @ApiSummary  (代币转账)
    * @ApiMethod   (POST)
    * @ApiRoute    (/api/eth/transfer)
    * @ApiParams   (name="privatekey", type="string", required=true, description="私钥")
    * @ApiParams   (name="contractaddress", type="string", required=true, description="合约地址")
    * @ApiParams   (name="value", type="string", required=true, description="代币数量")
    * @ApiParams   (name="to", type="string", required=true, description="收款账号")
    * @ApiParams   (name="gasprice", type="string", required=false, description="手续费参考0.0012 ETH‬")
    * @ApiParams   (name="unit", type="string", required=true, description="长度单位")
    * @ApiReturnParams   (name="code", type="integer", required=true, description="1")
    * @ApiReturnParams   (name="msg", type="string", required=true, description="返回成功")
    * @ApiReturnParams   (name="data", type="object", description="导入以太坊钱包返回对象，如果导入失败不存在则为null。导入钱包结构如下")
    * @ApiReturnParams   (name="PrivateKey", type="string", description="私钥")
    * @ApiReturnParams   (name="PublicKey", type="string", description="公钥")
    * @ApiReturnParams   (name="address", type="string", description="账号地址")
    * @ApiReturn   ({"code":1,"msg":"返回成功","time":"1576833785","data":{"PrivateKey":"caa5c611828cd7e8854cfe1cde6d54e6ecc5a673e742e36479797f99e422cca8","PublicKey":"04a36ea424b83c179d389a6c39fc1af7b07f1def4d347a6c93d94048d7d5c4837495fac856414bbd80d97be5e7e21bf94691884e69787701ad79a1056ce356c025","address":"0xf0e7d0ddd4411d01860f83e77c652c86ae31eb48"}})
    *
    */
    public function transfer()
    {
        $privatekey = input('privatekey');
        $contractaddress = input('contractaddress');
        $gasprice = input('gasprice');
        $value = input('value');
        $to = input('to');
        $unit = input('unit');
        $value = bcmul($value,bcpow(10,$unit));


        if (!$privatekey) {
            $this->error('必选参数');
        }
        if (strlen($privatekey) != 64) {
            $this->error('请确保地址有效长度');
        }

        //使用私钥创建Credential对象
        $credential = Credential::fromKey($privatekey);


        //单位
        if(!$gasprice){
            //单位
            $gasprice = $this->eth->gasPrice();
        }else{
             // $gasprice = '10000000000';
             $gasprice = strval(bcdiv(($gasprice * bcpow(10,9) ),21000) * bcpow(10,9));
        }

        $this->Transferrequest($credential->getAddress(),'ERC20', $gasprice,'60000');

        $receipt = $this->eth->transfer($credential, $contractaddress, $value, $to, $gasprice, $this->chainId);

        if ($receipt) {
        $this->success('转账中',$receipt);
        }
        $this->success('转账失败');
    }

    /**
    * 合约部署裸交易的生成与提交
    * @ApiSummary  (合约部署裸交易的生成与提交，暂时不对外开放, 合约ABI和bin生成，懂的话可以自己弄，不会就默认不填写。可以访问该网址生成http://remix.hubwiz.com)
    * @ApiMethod   (POST)
    * @ApiRoute    (/api/eth/testContractDeployTx)
    * @ApiParams   (name="privatekey", type="string", required=true, description="私钥)
    * @ApiParams   (name="name", type="string", required=true, description="代币的名称(1-28个符号。可接受英文字母、数字、字符、空格和连字符))
    * @ApiParams   (name="symbol", type="string", required=true, description="代币符号(1-10个字符,例如ETH、BTC、BAT等。不能包括空格，可以包括英文字母、数字字符等))
    * @ApiParams   (name="unit", type="string", required=true, description="小数位(定义代币的小数位数。接受0-18个数字。18是最常见的做法))
    * @ApiParams   (name="total", type="string", required=true, description="供应总量(总量需要生成的代币数量,最小数量是1, 最大是1000000000000000)")
    * @ApiParams   (name="abi", type="string", required=true, description="合约对象(默认可不选，可以自己生成添加)")
    * @ApiParams   (name="bin", type="string", required=true, description="合约字节码(默认可不选，可以自己生成添加)")
    * @ApiReturn   ({"code":1,"msg":"返回成功","time":"1576833785","data":{"contractaddress":"0xf0e7d0ddd4411d01860f83e77c652c86ae31eb48"}})
    *
    */
    public function testContractDeployTx()
    {
        // $name = input('name');
        // $symbol = input('symbol');
        // $unit = input('unit');
        // $total = input('total');
        // $abi = input('abi');
        // $bin = input('bin');

        $privatekey = input('privatekey');
        $parameter = input();
        if(empty(input('abi')) && empty(input('bin'))){
            $parameter['abi'] = file_get_contents(dirname(__FILE__).'./contract/build/EzToken.abi');
            $parameter['bin'] = '0x'.file_get_contents(dirname(__FILE__).'./contract/build/EzToken.bin');
        }

        $parameter['chainId'] = $this->chainId;
        //使用私钥创建Credential对象
        $credential = Credential::fromKey($privatekey);
        $receipt = $this->eth->testContractDeployTx($credential,$parameter,$this->eth->gasPrice());

        if ($receipt) {
            $this->success('发送成功，等待区块确认，生成合约地址',$receipt);
        }
        $this->error('发送失败');

    }

    /**
    * 查询账号代币余额
    * @ApiSummary  (查询账号代币余额)
    * @ApiMethod   (GET)
    * @ApiRoute    (/api/eth/tokenbalance)
    * @ApiParams   (name="contractaddress", type="string", required=true, description="合约地址")
    * @ApiParams   (name="address", type="string", required=true, description="账户地址")
    * @ApiParams   (name="unit", type="integer", required=false, description="长度单位" )
    * @ApiReturnParams   (name="code", type="integer", required=true, description="1")
    * @ApiReturnParams   (name="msg", type="string", required=true, description="返回成功")
    * @ApiReturnParams   (name="data", type="string", description="账号代币余额")
    * @ApiReturn   ({"code":1,"msg":"返回成功","time":"1577430508","data":"1000000"})
    *
    */
    public function Tokenbalance()
    {
        $contractaddress = $this->request->request('contractaddress');
        $address = $this->request->request('address');
        $unit = $this->request->request('unit');
        $unit = empty($unit) ? 0 : $unit;

        if (!$address || !$contractaddress) {
            $this->error('必选参数');
        }
        if (strlen($contractaddress) != 42) {
            $this->error('请确保地址有效长度');
        }

        $url = ethapi()."module=account&action=tokenbalance&address={$address}&contractaddress={$contractaddress}&tag=latest";

        try {
            $result =  json_decode(file_get_contents($url),true);
        } catch (\Throwable $th) {
            trace($th->getMessage(),'查询账号代币余额error');
            $this->error('系统错误，请联系管理员');
        }


        // dump($result);die;
        if ($result['status'] == 0) {
            $this->error($result['message']);
        }
        $this->success('返回成功',bcdiv($result['result'],pow(10,$unit),8) * 1);

    }

    /**
    * 查询账号代币余额(副本)
    * @ApiSummary  (查询账号代币余额)
    * @ApiMethod   (GET)
    * @ApiRoute    (/api/eth/tokenbalance)
    * @ApiParams   (name="contractaddress", type="string", required=true, description="合约地址")
    * @ApiParams   (name="address", type="string", required=true, description="账户地址")
    * @ApiParams   (name="unit", type="integer", required=true, description="长度单位")
    * @ApiReturnParams   (name="code", type="integer", required=true, description="1")
    * @ApiReturnParams   (name="msg", type="string", required=true, description="返回成功")
    * @ApiReturnParams   (name="data", type="string", description="账号代币余额")
    * @ApiReturn   ({"code":1,"msg":"返回成功","time":"1577430508","data":"1000000"})
    *
    */
    protected function Tokenbalance1()
    {
        $contractaddress = $this->request->request('contractaddress');
        $address = $this->request->request('address');
        $unit = $this->request->request('unit');

        if (empty($unit)) {
            $unit = 1;
        }elseif(!is_int($unit)){
            $unit = 10**$unit;
        }else{
            $this->error('输入必须为整数');
        }

        if (!$address || !$contractaddress) {
            $this->error('必选参数');
        }
        if (strlen($contractaddress) != 42) {
            $this->error('请确保地址有效长度');
        }
        $web3 = new Web3($this->host);
        $cb = new Callback;

        $contract = $this->loadContract($web3,$contractaddress,'EzToken');

        $balance = $this->balanceOf($contract,$address);
        $this->success('返回成功',bcdiv($balance,$unit));
    }

    // 加载合约
    protected function loadContract($web3,$contractaddress,$artifact)
    {
      $dir = __DIR__.'/contract/build/';
      $abi = file_get_contents($dir . $artifact . '.abi');
      $addr = file_get_contents($dir . $artifact . '.addr');
    //   $addr = $contractaddress;
      $contract = new Contract($web3->provider,$abi);
      $contract->at($addr);
    //   $contract->at($contractaddress);
      return $contract;
    }

    // 代币价格
    protected function balanceOf($contract,$account)
    {
      $cb = new Callback;
      $opts = [];
      $contract->call('balanceOf',$account,$opts,$cb);
      return $cb->result['balance']->toString();
    }

    /**
     * 转账是否满足要求
     *
     * @param [type] $walletAddress  地址
     * @param [type] $value 转账金额
     * @param string $gasprice 油价
     * @param string $gasLimit 步数上限
     * @return void
     */
    protected function Transferrequest($walletAddress,$value,$type = 'ETH',$gasprice = '10000000000',$gasLimit = '21000')
    {
         // 余额
         $getBalance = $this->eth->getBalance($walletAddress);
         $getBalance_ETH =  (bcdiv($getBalance,pow(10,18),6) * 1);
         if(!$getBalance)$this->error("可用余额为{$getBalance_ETH}ETH, 请保证地址帐号余额充足");

        // $this->whether_getBalance($walletAddress);
        // 转账金额
        $valueWei = Utils::toWei($value,'ether')->toString();
        if(empty($valueWei))$this->error('请输入转账金额');

        if($type == 'ETH'){
            // 可用余额 = 余额 - 转账金额
            $getBalance =  bcsub($getBalance,$valueWei);
        }
        // 手续费 = 价格 * gasLimit
        $fee = $gasprice * $gasLimit;
        // 剩余金额 = 可用余额 - 手续费
        $usable = bcsub($getBalance,$fee);
        // 判断可用余额，是否支付手续费
        if ($usable < 0) {
            $this->error("余额不足，不可支付矿工费，可用余额为{$getBalance_ETH}ETH");
        }
    }

}
